#include "termInverter.h"

static int qsort_compare(const void *s1, const void *s2)
{
  uint64_t *t1 = (uint64_t *) s1;
  uint64_t *t2 = (uint64_t *) s2;
  if(*t1 > *t2)
    return 1;
  else if(*t1 < *t2)
    return -1;
  else return 0;
}

int TermInverter::invert(char* in_fname, char* out_fname) {
  FILE* ifp;
  FILE* ofp;
  int ret;

  //NOTICE("now processing[%s]->[%s]",
  //    in_fname, out_fname);

  ifp = fopen(in_fname, "r");
  if(ifp == NULL) {
    //WARNING("not found:%s", in_fname);
    return RET_NULL;
  }
  ofp = fopen(out_fname, "w");
  if(out_fname == NULL) {
    WARNING("cannot fopen [%s]", out_fname);
    return E_RET;
  }

  while(1) {
    ret = process_one_link(ifp, ofp);
    if(ret == RET_EOF) {
      fclose(ifp);
      serialize(ofp);
      fclose(ofp);
      return S_RET;
    }
    else if(ret != S_RET) {
      WARNING("error processing link from [%s]",
	  ifp);
      return E_RET;
    }
  }


  assert(0);
}

int TermInverter::load() {
  tlink = new google::dense_hash_map<uint64_t , link_list>;
  if(tlink == NULL) {
    return E_RET;
  }
  uint64_t key0 = 0;
  tlink->set_empty_key(key0);
  m_size_alloc = 0;

  tsign_arr = (uint64_t*)malloc(TSIGN_ARR_NUM_INIT*sizeof(uint64_t));
  assert(tsign_arr);
  tsign_alloced = TSIGN_ARR_NUM_INIT;
  tsign_num = 0;

  return S_RET;
}

void TermInverter::reset() {
  //free all
  typedef google::dense_hash_map<uint64_t, link_list>::iterator Iter;
  Iter iter;
  link_node* pnode;
  link_node* pnode_next;

  for(iter = tlink->begin() ; iter != tlink->end() ; ++iter) {
    pnode = iter->second.head;
    pnode_next = pnode->next;
    while(1) {
      free(pnode);
      if(pnode_next == NULL) {
        break;
      }
      pnode = pnode_next;
      pnode_next = pnode->next;
    }
  }
  
  tlink->clear();
  tsign_num = 0;
  m_size_alloc = 0;
}

int TermInverter::process_one_link(FILE* ifp, FILE* ofp) {
  tl_head_t head;
  tl_term_t term;
  offset_t  offset[OFFSET_NUM];
  int ret;

  ret = fread(&head, sizeof(tl_head_t), 1, ifp);
  if(ret != 1) {
    if(feof(ifp)){
      return RET_EOF;
    }
    else {
      assert(0);
    }
  }

  //NOTICE("---head: id:%d num_term:%d---",
  //    head.id, head.num_term);

  for(int i = 0 ; i < head.num_term ; ++i) {
    ret = fread(&term, sizeof(tl_term_t), 1, ifp);
    assert(ret == 1);
  //  WARNING("\tterm[%llu] w[%f] num_off[%d]",
//	term.sign, term.weight, term.num_off);
    ret = fread(offset, sizeof(offset_t), term.num_off, ifp);
    assert(ret == term.num_off);

    addTerm(&head, &term, offset);
  }


  return S_RET;
}

link_node* TermInverter::alloc_new_node() {
  link_node* pnode;

  if(m_size_alloc + sizeof(link_list) 
      > MAX_NODE_ALLOC) {
    WARNING("alloc node exeeds max[%d]",
	MAX_NODE_ALLOC);
    return NULL;
  }

  pnode = (link_node*)malloc(sizeof(link_node));
  assert(pnode);

  return pnode;
}

int TermInverter::addTerm(tl_head_t* head, tl_term_t* term, offset_t* off) {
  link_node* pnode;
  typedef google::dense_hash_map<uint64_t, link_list>::iterator Iter;
  Iter iter;
  typedef std::pair<uint64_t, link_list> Pair;

  iter = tlink->find(term->sign);
  if(iter != tlink->end()) {
    //find, add to the end
//    NOTICE("find term:%u, id[%u]",
//	iter->first,
//	head->id);

    pnode = alloc_new_node();
    if(pnode == NULL) {
      return S_RET;
    }

    pnode->id = head->id;
    pnode->weight = term->weight;
    pnode->num_off = term->num_off;
    for(int i = 0 ; i < term->num_off ; ++i) {
      memcpy(&pnode->off[i], &off[i], sizeof(offset_t));
    }
    pnode->next = NULL;

    iter->second.num_node++;
    iter->second.num_off+=term->num_off;
    iter->second.tail->next = pnode;
    iter->second.tail = pnode;
  }
  else {
    //not find create new list
    link_list list;
    pnode = alloc_new_node();
    assert(pnode);

//    NOTICE("new term find[%u] add id[%u]", 
//	term->sign, head->id);
    pnode->id = head->id;
    pnode->weight = term->weight;
    pnode->num_off = term->num_off;
    for(int i = 0 ; i < term->num_off ; ++i) {
      memcpy(&pnode->off[i], &off[i], sizeof(offset_t));
    }
    pnode->next = NULL;

    list.num_node = 1;
    list.num_off  = term->num_off;
    list.head = pnode;
    list.tail = pnode;
    list.tsign = term->sign;

    if(tsign_num + 1 >= tsign_alloced) {
      tsign_arr = (uint64_t*)realloc(tsign_arr, 
	  tsign_alloced + TSIGN_ARR_NUM_INIT);
      if(tsign_arr == NULL) {
	FATAL("prog abnormal on reallocing memory");
	assert(0);
      }
      tsign_alloced+=TSIGN_ARR_NUM_INIT;
    }

    tsign_arr[tsign_num] = term->sign;
    tsign_num++;

    Pair pair(term->sign, list);
    std::pair<Iter, bool> pairInserted =
      tlink->insert(pair);
    if(pairInserted.second)
    {
      //      WARNING("add [%s] ok", p);
    }
    else {
      WARNING("add term[%u] error", 
	  term->sign);
      return E_RET;
    }
  }

  return S_RET;
}

int TermInverter::serialize(FILE* fp) {
  typedef google::dense_hash_map<uint64_t, link_list>::iterator Iter;
  Iter iter;
  IdxBuilder* ib = new IdxBuilder();
  assert(ib);

  qsort(tsign_arr, tsign_num, sizeof(uint64_t), qsort_compare);

  NOTICE("---start---");

  for(uint32_t i = 0 ; i < tsign_num ; ++i) {
    iter = tlink->find(tsign_arr[i]);
    if(iter == tlink->end()) {
      FATAL("prog abnormal, hash lost");
      assert(0);
    }
    DEBUG("output term[%u], linknum[%d]", 
	iter->first,
	iter->second.num_node);
    ib->build_idx_info(&iter->second);
    ib->dump_idx_info(fp);
    ib->free_idx_info();
  }

  return S_RET;
}
